<template>
  <el-table-column v-bind="$attrs">
    <template slot="header">
      <div class="d-flex a-center">
        操作
        <i
          v-if="Object.keys(rowData).length"
          class="el-icon-circle-plus font-18 mr-l5 pointer main-color"
          @click="addMain"
        />
      </div>
    </template>

    <template slot-scope="{ row, $index }">
      <slot name="before" :row="row" :$index="$index" />
      <el-link
        v-show="
          !row.isEdit && layout.includes('edit') && custOperHandle(row)['edit']
        "
        :underline="false"
        type="primary"
        class="font-13"
        @click="editMain(row, $index)"
      >编辑</el-link>
      <el-link
        v-show="row.isEdit && layout.includes('save')"
        :underline="false"
        type="success"
        class="font-13"
        @click="saveMain(row, $index)"
      >保存</el-link>
      <el-link
        v-show="row.isEdit && row[primaryKey] && layout.includes('cancel')"
        :underline="false"
        type="info"
        class="mr-l10 font-13"
        @click="cancelMain(row, $index)"
      >取消</el-link>
      <el-link
        v-show="
          (!row.isEdit || !row[primaryKey]) &&
            layout.includes('delete') &&
            custOperHandle(row)['delete']
        "
        :underline="false"
        type="danger"
        class="mr-l10 font-13"
        @click="deleteMain(row, $index)"
      >删除</el-link>
      <slot name="after" :row="row" :$index="$index" />
    </template>
  </el-table-column>
</template>

<script>
// import * as API from '@/api/columnEditApi/columnEditApi.js'
const API = {}
import { throttle } from './util.js'
export default {
  name: 'LtTableColumnOperation',
  props: {
    // 表格数据
    value: {
      type: null,
      default: null
    },
    // 是否自定义编辑
    customEdit: {
      type: Boolean,
      default: false
    },
    // 是否添加时为头部插入
    unshift: {
      type: Boolean,
      default: true
    },
    // 行数据的标准格式
    rowData: {
      type: Object,
      default: () => ({})
    },
    // 布局
    layout: {
      type: Array,
      default: () => ['save', 'delete', 'edit', 'cancel']
    },
    // 删除操作对应的api
    deleteApi: {
      type: String,
      default: ''
    },
    // 数据的主键
    primaryKey: {
      type: String,
      default: 'id'
    },
    // 需要判断空的字段
    validateProp: {
      type: [Array, Boolean, String],
      default: () => []
    },
    // 正则校验
    validateReg: {
      type: Array,
      default: () => []
    },
    // 范围校验
    validateRange: {
      type: Array,
      default: () => []
    },
    // 内置保存操作对应的api
    saveApi: {
      type: String,
      default: ''
    },
    // 内置编辑操作对应的api
    editApi: {
      type: String,
      default: ''
    },
    // 自定义校验
    custValidate: {
      type: [String, Function],
      default: ''
    },
    // 是否可以无限添加
    unlimitedAdd: {
      type: Boolean,
      default: false
    },
    // 自定义操作列的显示
    custOperDisplay: {
      type: [String, Function],
      default: ''
    },
    addBefore: {
      type: Function,
      default: () => {}
    }
  },
  data() {
    return {
      rowMapCopy: {} // 行数据的备份,key为数据主键,value为行数据
    }
  },
  computed: {
    tableData: {
      get() {
        return this.value
      },
      set(val) {
        this.$emit('input', val)
      }
    }
  },
  methods: {
    /**
     * 添加
     */
    addMain() {
      // 判断是否允许无限新增
      if (!this.unlimitedAdd) {
        for (let i = 0; i < this.tableData.length; i++) {
          if (!this.tableData[i][this.primaryKey]) {
            this.$message({ message: `请先保存第${i + 1}条数据` })
            return
          }
        }
      }

      // 对rowData追加isEdit字段
      const row = { ...this.rowData }
      row.isEdit = false

      if (this.addBefore && typeof this.addBefore === 'function') { this.addBefore(row) }

      // 追加至表格数组中
      this.unshift ? this.tableData.unshift(row) : this.tableData.push(row)
      const activeIndex = this.unshift ? 0 : this.tableData.length - 1
      this.editMain(this.tableData[activeIndex], activeIndex)
    },
    /**
     * 编辑
     * @param {Object} row      当前行数据
     * @param {Object} index    当前行索引
     */
    editMain(row, index) {
      this.$set(row, 'isEdit', true)
      this.$forceUpdate()
      // 保存一份数据,用于取消时的数据还原
      if (row[this.primaryKey]) {
        this.rowMapCopy[row[this.primaryKey]] = JSON.parse(JSON.stringify(row))
      }

      // 自动聚焦
      this.autoFocus(index)
      this.$nextTick(() => {
        this.$emit('edit-confirm', {
          row,
          index
        })
      })
    },
    /**
     * 编辑初始化,由外部通过ref手动调用
     * @param {Object} index
     */
    editInit(index) {
      const row = this.tableData[index]
      this.$set(row, 'isEdit', true)
      this.$forceUpdate()
      // 保存一份数据,用于取消时的数据还原
      if (row[this.primaryKey]) {
        this.rowMapCopy[row[this.primaryKey]] = JSON.parse(JSON.stringify(row))
      }

      // 自动聚焦
      this.autoFocus(index)
      this.$nextTick(() => {
        this.$emit('edit-confirm', {
          row,
          index
        })
      })
    },
    /**
     * 保存
     * @param {Object} row      当前行数据
     * @param {Object} index    当前行索引
     */
    saveMain: throttle(function(row, index) {
      // 1.校验层
      // 筛选条件
      let validatePropList = []
      const condition = (item) => {
        // 字段范围
        let flag = true

        if (typeof this.validateProp === 'string') {
          validatePropList = this.validateProp
            .replace(/\s+/g, '')
            .split(',')
            .filter((item) => item)
        }
        if (['boolean', 'object'].includes(typeof this.validateProp)) { validatePropList = this.validateProp }

        // 一个都不校验
        if (validatePropList === false) flag = false
        // 全部校验
        if (validatePropList.length === 0) flag = true
        // 只校验调用者指定的
        if (validatePropList.length) { flag = validatePropList.includes(item.prop) }

        return item.$options.name === 'LtTableColumnEdit' && flag
      }
      const columnEditList = this.$parent.$children.filter((item) =>
        condition(item)
      )
      // 1.1非空判断
      if (validatePropList && columnEditList.length) {
        // 当前挂载的编辑列的所有prop的集合
        const props = columnEditList.map((item) => item.prop)
        const i = props.findIndex(
          (item) =>
            item in row &&
            (row[item] === '' || row[item] === null || row[item] === undefined)
        )
        if (i !== -1) {
          this.$message({
            message: `${columnEditList[i]['label']}不能为空,${
              columnEditList[i]['state'] === 'input' ? '请填写' : '请选择'
            }`
          })
          // 自动聚焦
          this.autoFocus(index, props[i])
          return
        }
      }

      // 1.2正则校验字段
      if (this.validateReg.length) {
        const i = this.validateReg.findIndex(
          (item) => !item['reg'].test(row[item['prop']])
        )
        if (i !== -1) {
          const label = this.$parent.$children.filter(
            (item) =>
              item.$options.name === 'LtTableColumnEdit' &&
              item.prop === this.validateReg[i].prop
          )[0].label
          this.$message({
            message: this.validateReg[i].msg || `${label}数据格式不正确,请检查`
          })
          this.autoFocus(index, this.validateReg[i]['prop'])
          return
        }
      }

      // 1.3校验范围
      if (this.validateRange.length) {
        // 范围校验
        const rangeCheck = (item) => {
          if ('min' in item && 'max' in item) {
            return (
              row[item['prop']] < item.min || row[item['prop']] > item['max']
            )
          }
          if ('min' in item) return row[item['prop']] < item.min
          if ('max' in item) return row[item['prop']] > item.max
          return false
        }

        // 范围校验提示
        const rangeMessage = (label, item) => {
          if ('min' in item && 'max' in item) { return `${label}的范围为${item.min}至${item.max}` }
          if ('min' in item) return `${label}最小值为${item.min}`
          if ('max' in item) return `${label}最大值为${item.max}`
        }

        const i = this.validateRange.findIndex((item) => rangeCheck(item))
        if (i !== -1) {
          const label = this.$parent.$children.filter(
            (item) =>
              item.$options.name === 'LtTableColumnEdit' &&
              item.prop === this.validateRange[i].prop
          )[0].label
          this.$message({
            message:
              this.validateRange[i].msg ||
              rangeMessage(label, this.validateRange[i])
          })
          this.autoFocus(index, this.validateRange[i]['prop'])
          return
        }
      }

      // 1.4重复性校验
      const repeatColumnList = this.$parent.$children.filter(
        (item) => item.$options.name === 'LtTableColumnEdit' && item.noRepeat
      )
      if (repeatColumnList.length) {
        const i = repeatColumnList.findIndex((item) => {
          // 遍历表格数据,判断其他数据是否有一样的
          for (let j = 0; j < this.tableData.length; j++) {
            if (this.tableData[j][item.prop] === row[item.prop] && j !== index) { return true }
          }
          return false
        })
        if (i !== -1) {
          const options = repeatColumnList[i].selectOption.options
          const value = repeatColumnList[i].selectOption.value
          const label = repeatColumnList[i].selectOption.label

          const tips =
            repeatColumnList[i].state === 'select'
              ? options.filter(
                (item) => row[repeatColumnList[i].prop] === item[value]
              )[0][label]
              : row[repeatColumnList[i].prop]
          this.$message({
            message:
              repeatColumnList[i].label + '不能出现重复值，提示：' + tips
          })
          return
        }
      }
      // 1.5自定义校验
      if (this.custValidate !== '' && !this.custValidate(row)) return

      // 内置的保存
      if (this.saveApi || this.editApi) {
        const api =
          this.saveApi && this.editApi
            ? row[this.primaryKey]
              ? this.editApi
              : this.saveApi
            : this.saveApi

        this.$emit('request-before', {
          row,
          index
        })

        API[api](row)
          .then((res) => {
            this.$notify.success({ message: '保存成功' })
            this.$emit('save-confirm', true)
            // 主键更新
            if (
              typeof res === 'object' &&
              res.data &&
              res.data[this.primaryKey]
            ) { row[this.primaryKey] = res.data[this.primaryKey] }

            row.isEdit = false
          })
          .catch((e) => {
            this.$notify.error({ message: '保存失败' })
            this.$emit('save-confirm', false)
          })
        return
      }
      // 自定义保存操作
      this.$emit('save', {
        row,
        index
      })
    }, 500),
    /**
     * 取消
     * @param {Object} row      当前行数据
     * @param {Object} index    当前行索引
     */
    async cancelMain(row, index) {
      let flag = false
      // 当前行数据
      const activeRow = JSON.parse(JSON.stringify(row))
      const rowCopy = JSON.parse(
        JSON.stringify(this.rowMapCopy[row[this.primaryKey]])
      )
      // 1.先剔除可能存在的排序字段
      if ('sortField' in this.$parent.$parent) {
        delete activeRow[this.$parent.$parent.sortField]
        delete rowCopy[this.$parent.$parent.sortField]
      }

      // 2.再判断是否进行了更改
      if (JSON.stringify(activeRow) !== JSON.stringify(rowCopy)) {
        await this.$confirm('此操作将对数据进行还原,您确定吗?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })
          .then(() => {
            // 数据对比
            for (const key in row) {
              if (key !== 'isEdit' && row[key] !== rowCopy[key]) {
                // 数据还原
                row[key] = rowCopy[key]
              }
            }
            row.isEdit = !row.isEdit
            flag = true
          })
          .catch(() => {
            flag = true
          })
      }
      if (flag) return
      // eslint-disable-next-line require-atomic-updates
      row.isEdit = !row.isEdit
    },
    /**
     * 删除
     * @param {Object} row      当前行数据
     * @param {Object} index    当前行索引
     */
    deleteMain: throttle(function(row, index) {
      // 前端删除
      if (!row[this.primaryKey]) {
        this.tableData.splice(index, 1)
        this.$notify.success({ message: '删除成功' })
        return
      }

      this.$confirm('是否删除这条记录?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(() => {
          if (this.deleteApi) {
            API[this.deleteApi](row[this.primaryKey])
              .then((res) => {
                this.$notify.success({ message: '删除成功' })
                this.tableData.splice(index, 1)
                this.$emit('delete-confirm', true)
              })
              .catch((e) => {
                this.$notify.error({ message: '删除失败' })
                this.$emit('delete-confirm', false)
              })
          } else {
            this.$emit('delete', {
              row,
              index
            })
          }
        })
        .catch(() => {})
    }, 500),
    /**
     * 自动聚焦
     * @param {Object} index  索引
     * @param {Object} prop   字段
     */
    autoFocus(index, prop = '') {
      // 获取所有的lt-table-column-edit组件 && 为input
      let columnEditList = this.$parent.$children.filter(
        (item) =>
          item.$options.name === 'LtTableColumnEdit' &&
          item.state === 'input' &&
          !item.editDisable
      )

      if (columnEditList && columnEditList.length) {
        const inputFocus = (index, prop) => {
          this.$nextTick(() => {
            // 可能存在自定义编辑插槽,所以此处需要做判断
            if (
              document.querySelector(
                `input[cust-id=${prop}-${index}-${this.$parent._uid}]`
              )
            ) {
              document
                .querySelector(
                  `input[cust-id=${prop}-${index}-${this.$parent._uid}]`
                )
                .focus()
            }
          })
        }

        // 内部校验时的指定字段聚焦
        if (prop) {
          if (
            columnEditList &&
            columnEditList.filter((item) => item.prop === prop).length
          ) { inputFocus(index, prop) }
          return
        }

        // 调用者外部指定的focus列聚焦
        if (columnEditList.filter((item) => item.focus).length) {
          columnEditList = columnEditList.filter((item) => item.focus)
          inputFocus(index, columnEditList[0].prop)
          return
        }

        // 最后则是默认取第一个input列进行自动聚焦
        inputFocus(index, columnEditList[0].prop)
      }
    },
    /**
     * 自定义操作列的处理
     * @param {Object} row
     */
    custOperHandle(row) {
      const defalutData = {
        edit: true,
        delete: true
      }

      if (!this.custOperDisplay) return defalutData

      return { ...defalutData, ...this.custOperDisplay(row) }
    },
    /**
     * 保存完成
     * @param {Object} row          行数据
     * @param {Object} primaryKey   主键
     */
    saveComplete(row, primaryKey = '') {
      if (primaryKey) row[this.primaryKey] = primaryKey
      row.isEdit = false
    },
    /**
     * 删除完成
     * @param {Object} index
     */
    deleteComplete(index) {
      this.tableData.splice(index, 1)
    }
  }
}
</script>

<style scoped>
.font-13 {
  font-size: 13px !important;
}
.font-18 {
  font-size: 18px !important;
}
.mr-l5 {
  margin-left: 5px !important;
}
.pointer {
  cursor: pointer !important;
}
.mr-r10 {
  margin-right: 10px !important;
}
.mr-l10 {
  margin-left: 10px !important;
}
.d-flex {
  display: flex;
}
.a-center {
  align-items: center;
}
.main-color {
  color: #1890ff;
}
</style>
